package jaeger

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	us "net/url"
	"time"
)

func jaegerTracesK8s(url, guid string, lookback int64, data interface{}) error {
	var (
		limit = 1
		end   = time.Now().Unix() * 1000000
		start = end - lookback*1000000
	)

	servicsName, err := getServiceNames(url)
	if err != nil {
		return fmt.Errorf("get service name %v", err)
	}

	for _, v := range servicsName {
		u := fmt.Sprintf(`http://%v/api/traces?service=%v&start=%v&end=%v&&lookback=custom&limit=%v&tags=%%7B"guid"%%3A"%v"%%7D`,
			url, us.QueryEscape(v), start, end, limit, guid)

		tracesReq, err := http.Get(u)
		if err != nil {
			return fmt.Errorf("request jaeger %v", err)
		}
		bts, err := ioutil.ReadAll(tracesReq.Body)
		if err != nil {
			return fmt.Errorf("read trace body %v", err)
		}

		if err = extraTrace(bts, data); err != nil {
			return fmt.Errorf("extra trace %v", end)
		}
	}

	return nil
}

func jaegerTracesGluenets(url, guid string, containers []string, lookback int64, data interface{}) error {
	var (
		limit = 1
		end   = time.Now().Unix() * 1000000
		start = end - lookback*1000000
	)

	servicsName, err := getServiceNames(url)
	if err != nil {
		return fmt.Errorf("get service name %v", err)
	}

	for _, v := range servicsName {
		for _, ct := range containers {
			u := fmt.Sprintf(`http://%v/api/traces?service=%v&start=%v&end=%v&&lookback=custom&limit=%v&tags=%%7B"hostname"%%3A"%v"%%7D`,
				url, us.QueryEscape(v), start, end, limit, guid+"_"+ct)

			tracesReq, err := http.Get(u)
			if err != nil {
				return fmt.Errorf("request jaeger %v", err)
			}
			bts, err := ioutil.ReadAll(tracesReq.Body)
			if err != nil {
				return fmt.Errorf("read trace body %v", err)
			}

			if err = extraTrace(bts, data); err != nil {
				return fmt.Errorf("extra trace %v", end)
			}
		}
	}

	return nil
}

func extraTrace(bts []byte, data interface{}) error {
	var err error

	ts := make(map[string]interface{})
	if err = json.Unmarshal(bts, &ts); err != nil {
		return fmt.Errorf("decode trace %v", err)
	}

	// dorp the error trace
	if errs, ok := ts["errors"]; ok && errs != nil {
		return nil
	}
	for _, atri := range ts["data"].([]interface{}) {
		atr, ok := atri.(map[string]interface{})
		if !ok {
			return fmt.Errorf("trace data is not map[string]interface{}")
		}

		if warning, ok := atr["warnings"]; ok && warning != nil {
			continue
		}

		tid, ok := atr["traceID"]
		if !ok {
			return fmt.Errorf("trace data not contain traceID")
		}
		spans, ok := atr["spans"]
		if !ok {
			return fmt.Errorf("trace data not contain spans")
		}

		drop := false
		for _, spi := range spans.([]interface{}) {
			sp, ok := spi.(map[string]interface{})
			if !ok {
				return fmt.Errorf("span data is not map[string]interface{}")
			}
			if warning, ok := sp["warnings"]; ok && warning != nil {
				drop = true
			}
		}
		if drop {
			continue
		}
		data.(map[string]interface{})[tid.(string)] = atr
	}
	return nil
}

func getServiceNames(url string) ([]string, error) {
	var (
		err         error
		serviceReq  *http.Response
		servicsName []string
	)
	if serviceReq, err = http.Get(fmt.Sprintf(`http://%v/api/services`, url)); err != nil {
		return nil, err
	}

	if servicsName, err = decodeServiceNames(serviceReq.Body); err != nil {
		return nil, err
	}
	return servicsName, nil
}

func decodeServiceNames(req io.Reader) ([]string, error) {
	var (
		err error
		bts []byte
	)

	if bts, err = ioutil.ReadAll(req); err != nil {
		return nil, err
	}

	var servics = make(map[string]interface{})
	if err = json.Unmarshal(bts, &servics); err != nil {
		return nil, err
	}

	data, ok := servics["data"]
	if !ok {
		return nil, fmt.Errorf("can't find service data")
	}

	if data == nil {
		return nil, nil
	}

	var servicsName = make([]string, len(data.([]interface{})))
	for k, v := range servics["data"].([]interface{}) {
		servicsName[k] = v.(string)
	}

	return servicsName, nil
}
